package org.pathirage.cloudbi.topk; import backtype.storm.task.IMetricsContext; import com.clearspring.analytics.stream.frequency.CountMinSketch; import com.google.common.collect.AbstractIterator; import org.apache.commons.lang3.tuple.Pair; import redis.clients.jedis.Jedis; import storm.trident.state.OpaqueValue; import storm.trident.state.State; import storm.trident.state.StateFactory; import storm.trident.state.map.IBackingMap; import storm.trident.state.map.OpaqueMap; import java.nio.ByteBuffer; import java.util.*; public class HourlyTopKBackingMap implements IBackingMap<OpaqueValue> { private Jedis redisClient; public HourlyTopKBackingMap(Map conf){ redisClient = new Jedis("localhost"); } public static StateFactory FACTORY = new StateFactory() { @Override public State makeState(Map conf, IMetricsContext metrics, int partitionIndex, int numPartitions) { return OpaqueMap.build(new HourlyTopKBackingMap(conf)); } }; @Override public List<OpaqueValue> multiGet(List<List<Object>> keys) { List<String> singleKeys = toSingleKeys(keys); List<OpaqueValue> values = new ArrayList<OpaqueValue>(); for(String key : singleKeys){ Map<byte[], byte[]> fields = redisClient.hgetAll(key.getBytes()); values.add(new OpaqueValue( ByteBuffer.wrap(fields.get("txid".getBytes())).getLong(), CountMinSketch.deserialize(fields.get("curr".getBytes())), CountMinSketch.deserialize(fields.get("prev".getBytes())))); } return values; } @Override public void multiPut(List<List<Object>> keys, List<OpaqueValue> vals) { List<String> singleKeys = toSingleKeys(keys); for(Pair<String, OpaqueValue> pair : zip(singleKeys, vals)){ byte[] prev = CountMinSketch.serialize((CountMinSketch) pair.getValue().getPrev()); byte[] cur = CountMinSketch.serialize((CountMinSketch) pair.getValue().getCurr()); long currTxid = pair.getValue().getCurrTxid(); Map<byte[],byte[]> fields = new HashMap<byte[], byte[]>(); fields.put("txid".getBytes(), ByteBuffer.allocate(8).putLong(currTxid).array()); fields.put("prev".getBytes(), prev); fields.put("curr".getBytes(), cur); redisClient.hmset(pair.getKey().getBytes(), fields); } } public static <L,R> Iterable<Pair<L,R>> zip(final Iterable<L> leftCol, final Iterable<R> rightCol) { return new Iterable<Pair<L,R>>() { @Override public Iterator<Pair<L, R>> iterator() { final Iterator<L> leftItr = leftCol.iterator(); final Iterator<R> rightItr = rightCol.iterator(); return new AbstractIterator<Pair<L,R>>() { @Override protected Pair<L, R> computeNext() { if (leftItr.hasNext() && rightItr.hasNext()) { return Pair.of(leftItr.next(), rightItr.next()); } else { return endOfData(); } }}; }}; } private List<String> toSingleKeys(List<List<Object>> keys) { List<String> singleKeys = new ArrayList<String>(); for(List<Object> key : keys){ // Single key because groupBy by hourSinceEpoch singleKeys.add((String)key.get(0)); } return singleKeys; } }